package com.example.userservice.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.constants.FileConstants;
import com.example.common.domain.Result;
import com.example.common.domain.pojo.User;
import com.example.common.enums.CodeStatus;
import com.example.common.utils.*;
import com.example.userservice.Constants.OtherConstants;
import com.example.userservice.Constants.RedisConstants;
import com.example.userservice.mapper.UserMapper;
import com.example.userservice.domain.dto.UsTokenDto;
import com.example.userservice.domain.vo.UserTokenVo;
import com.example.userservice.service.AuthService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service
public class AuthServiceImpl extends ServiceImpl<UserMapper, User> implements AuthService {
//    @Autowired
//    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private CacheClient cacheClient;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ObjectMapper objectMapper; // 用于解析 JSON 字符串
//    @Autowired
//    private SendSmsExample sendSmsExample;
    @Autowired
//    @Qualifier("aliOssUtilConfig") // 指定Bean名称
    private AliOssUtil aliOssUtil;

    public Map<String, String> AddToken(String id, String role) {
        Map<String, String> toKenMap = new HashMap<>();

        // 生成 Access Token 和 Refresh Token
        String accessToken = JwtUtil.generateToken(role, RedisConstants.ACCESS_TOKEN_TTL, role);   // 短的
        String refreshToken = JwtUtil.generateToken(role, RedisConstants.REFRESH_TOKEN_TTL, role);  // 长的

        // 存储用户 Token 列表和角色
        UsTokenDto userTokens = new UsTokenDto();
        userTokens.setAccessToken(accessToken);
        userTokens.setRefreshToken(refreshToken);
        userTokens.setRole(role);

        try {
            String value = objectMapper.writeValueAsString(userTokens); // 将对象转换为 JSON 字符串
            cacheClient.set(RedisConstants.LOGIN_USER_KEY + id, value, RedisConstants.ACCESS_TOKEN_TTL, TimeUnit.SECONDS); // 设置 30 天过期
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("无法存储用户令牌: " + id);
        }
        // 返回 Token
        toKenMap.put("accessToken", accessToken);
        toKenMap.put("refreshToken", refreshToken);
        return toKenMap;
    }

    public UserTokenVo ParseTokenRole(String id) {
        String usToken = cacheClient.get(id);  //通过id在redis获取权限
        if (usToken == null) {
            return null;
        }
        // 5.将查询到的hash数据转为UserDTO
        ObjectMapper objectMapper = new ObjectMapper();
        UsTokenDto usTokenDto = objectMapper.convertValue(usToken, UsTokenDto.class);
        UserTokenVo userTokenVo = new UserTokenVo(new User(id), AddToken(id, usTokenDto.getRole()),null,null);
        return userTokenVo;
    }

    public boolean logOut(String id) {        //移除key(退出登录)
        String key = RedisConstants.LOGIN_CODE_KEY + id;
        cacheClient.delete(key);
        return true;
    }

    public void sendLoginCode(String phone) {
        String code = SendSmsExample.generateVerificationCode(OtherConstants.MailNumber);   //生成验证码
        // 存储到Redis，设置5分钟过期
        String key = RedisConstants.LOGIN_CODE_KEY + phone;
        cacheClient.set(key, code, RedisConstants.CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        // 发送短信验证码
        SendSmsExample.SendCode(phone, code);
    }


    public void getBingPhoneCode(String phone) {
        String code = SendSmsExample.generateVerificationCode(OtherConstants.MailNumber);   //生成验证码
        System.out.println("getBingPhoneCode:"+code);
        // 存储到Redis，设置5分钟过期
        String key = RedisConstants.BING_PHONE_CODE_KEY_PREFIX + phone;
        cacheClient.set(key, code, RedisConstants.BING_PHONE_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        // 发送短信验证码
        SendSmsExample.SendCode(phone, code);
    }

    public Result<Object> BingPhone(String id, String phone, String code) {
        if (verifyBingCode(RedisConstants.BING_PHONE_CODE_KEY_PREFIX + phone, code)) {
            User user = userMapper.getPhone(phone);
            if (user != null) {
                return Result.error("该手机号已被绑定");
            }
            User user1 = getUserById(id);
            user1.setPhone(phone);
            if (userMapper.updateById(user1) == 1) {
                return Result.success("绑定成功");
            }
            return Result.error("绑定失败");
        }
        return Result.error("验证码错误或者验证码过期");
    }

    // 验证
    public boolean verifyBingCode(String key, String code) {
        // 从 Redis 获取存储的值
        String storedCode = cacheClient.get(key);
        System.out.println("verifyBingCode:"+storedCode);
        // 如果存储的值为空（表示已过期或不存在），返回 false
        if (storedCode != null && storedCode.equals(code)) {
            // 验证通过后删除验证码，防止重复使用
            cacheClient.delete(key);
            return true;
        }
        return false;
    }


    // 前端登录验证
    public boolean verifyLoginCode(String key, String code) {
        // 从 Redis 获取存储的值
        String storedCode = cacheClient.get(RedisConstants.LOGIN_CODE_KEY + key);
        // 如果存储的值为空（表示已过期或不存在），返回 false
        if (storedCode != null && storedCode.equals(code)) {
            // 验证通过后删除验证码，防止重复使用
            cacheClient.delete(key);
            return true;
        }
        return false;
    }


    // 发送修改手机号验证码
    public Result<Object> sendOldPhoneCode(String userId, String oldPhone) {
        // 生成验证码
        String code = SendSmsExample.generateVerificationCode(OtherConstants.MailNumber);
        // 存储Old手机号和验证码的关联
        String key = RedisConstants.OLD_PHONE_KEY_PREFIX + userId+oldPhone;
        System.out.println("sendOldPhoneCode:"+key);
        cacheClient.set(key, code, RedisConstants.CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        // 发送短信验证码
        SendSmsExample.SendCode(oldPhone, code);
        return Result.success();
    }

    public Result<Object> OldPhone(String userId, String oldPhone, String code) {
        String key = RedisConstants.OLD_PHONE_KEY_PREFIX + userId+oldPhone;
        String storedPhone = cacheClient.get(key);
        System.out.println("OldPhone:"+storedPhone);
      if(storedPhone!=null&&storedPhone.equals(code))
          return Result.success("验证成功");
        return Result.error(CodeStatus.NOT_FOUND.getCode(),"验证码或手机号码错误");
    }

    // 发送修改手机号验证码
    public Result<Object> sendModifyPhoneCode(String userId, String newPhone) {
        // 检查是否有正在进行的手机号修改操作
        String lockKey = RedisConstants.PHONE_MODIFICATION_LOCK_KEY + userId;
        Boolean isLocked = cacheClient.setIfAbsent(lockKey, "1", RedisConstants.LOCK_EXPIRE_MINUTES, TimeUnit.MINUTES);

        if (isLocked == null || !isLocked) {
            return Result.error("已有正在进行的手机号修改操作，请稍后再试");
        }

        // 生成验证码
        String code = SendSmsExample.generateVerificationCode(OtherConstants.MailNumber);

        // 存储新手机号和验证码的关联
        String key = RedisConstants.MODIFY_PHONE_CODE_KEY_PREFIX + userId;
        cacheClient.set(key, code, RedisConstants.CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        // 存储新手机号和用户ID的关联，用于后续验证
        String phoneKey = RedisConstants.NEW_PHONE_KEY_PREFIX + userId;
        cacheClient.set(phoneKey, newPhone, RedisConstants.CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        // 发送短信验证码
        SendSmsExample.SendCode(newPhone, code);
        return Result.success();
    }

    public Result<Object> modifyPhone(String userId, String newPhone, String code) {
        String key = RedisConstants.MODIFY_PHONE_CODE_KEY_PREFIX + userId;
        String storedCode = cacheClient.get(key);
        System.out.println("modifyPhone:"+storedCode);
        String phoneKey = RedisConstants.NEW_PHONE_KEY_PREFIX + userId;
        String storedPhone = cacheClient.get(phoneKey);
        System.out.println("modifyPhone:"+storedPhone);
        // 验证验证码和手机号是否匹配
        if (storedCode != null && storedCode.equals(code) &&
                storedPhone != null && storedPhone.equals(newPhone)) {
            // TODO: 更新用户手机号到数据库
            // 清理Redis中的临时数据
            cacheClient.delete(key);
            cacheClient.delete(phoneKey);
            cacheClient.delete(RedisConstants.PHONE_MODIFICATION_LOCK_KEY + userId);
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getId, userId);
            User updateUser = new User();
            updateUser.setPhone(newPhone);
            boolean updateResult = update(updateUser, queryWrapper);
            if (updateResult) {
                return Result.success("修改成功");
            }
            return Result.success("修改失败");
        }
        return Result.error("验证码或手机号码错误");
    }

    public Result<Object> sendResetPasswordCode(String phone) {
        // 检查手机号是否存在
        User user = userMapper.getPhone(phone);
        if (user == null) {
            return Result.error("该手机号未注册");
//            throw new RuntimeException("该手机号未注册");
        }

        // 生成验证码
        String code = SendSmsExample.generateVerificationCode(OtherConstants.MailNumber);

        // 存储到Redis
        String key = RedisConstants.RESET_PASSWORD_CODE_KEY_PREFIX + phone;
        cacheClient.set(key, code, RedisConstants.RESET_PASSWORD_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        // 发送短信
        SendSmsExample.SendCode(phone, code);
        return Result.success();
    }

    public Result<Object> resetPassword(String phone, String code, String newPassword) throws NoSuchAlgorithmException {
        String key = RedisConstants.RESET_PASSWORD_CODE_KEY_PREFIX + phone;
        String storedCode = cacheClient.get(key);
        if (storedCode != null && storedCode.equals(code)) {
            // 验证通过，更新密码
            User user = userMapper.getPhone(phone);
            if (user != null) {
                // 加密新密码
                String encryptedPassword = MD5Util.encrypt(newPassword);
                user.setPassword(encryptedPassword);
                userMapper.updateById(user);
                // 清除验证码
                cacheClient.delete(key);
                return Result.success();
            }
        }
        return Result.error("验证码错误或者用户消息不存在");
    }

    public User getPhone(String phone) {
        return userMapper.getPhone(phone);
    }

    public User PasToUser(String id, String password) {
        return userMapper.selectByUsernameAndPassword(id, password);
//        return query()
//                .eq("id", id)
//                .eq("password", password)
//                .one();
    }

    public boolean updatePassword(String id, String MD5Password) {
        User user = getById(id);
        if (user == null) {
            return false; // 用户不存在
        }
        // 设置加密后的密码到用户对象
        user.setPassword(MD5Password);
        // 更新数据库中的用户信息
        return updateById(user);
    }

    public boolean updateUser(User user) {
        if (user.getId() == null) {
            return false; // ID 必须存在
        }
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", user.getId());
        // 手动添加非null字段到set条件
        if (user.getUsername() != null) {
            updateWrapper.set("username", user.getUsername());
        }
        if (user.getImgUrl() != null) {
            updateWrapper.set("img_url", user.getImgUrl());
        }
        return userMapper.update(null, updateWrapper) > 0;
    }

    public User getUserById(String id) {
        return getById(id);
    }

    @Transactional
    public Result<Object> updateAvatar(String id, MultipartFile avatarFile) throws NoSuchAlgorithmException {
        // 4. 检查文件大小（示例：限制为2MB）
        if (avatarFile.getSize() > 2 * 1024 * 1024) {
            return Result.error("文件大小超出限制");
        }
        //检查文件类型（示例：仅允许图片格式）
        String contentType = avatarFile.getContentType();
        if (contentType == null || !isValidImageType(contentType)) {
            return Result.error("不支持的文件类型，仅允许图片格式");
        }
        try {
            String imgUrl = aliOssUtil.uploadImgUser(avatarFile,FileConstants.USER_IMG_PATH);
            // 3. 方式一：使用LambdaQueryWrapper更新用户表中的头像URL
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getId, id);

            User updateUser = new User();
            updateUser.setImgUrl(imgUrl);
            boolean updateResult = update(updateUser, wrapper);
            if (!updateResult) {
                // 数据库更新失败，删除OSS上的图片
                aliOssUtil.deleteFile(imgUrl.substring(imgUrl.lastIndexOf("/") + 1));
                return Result.error("用户信息不存在，头像更新失败");
            }
            return Result.success(imgUrl);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 校验是否为有效图片类型
     */
    private boolean isValidImageType(String contentType) {
        return contentType.startsWith("image/") ||
                ("image/jpeg".equals(contentType) ||
                        "image/png".equals(contentType) ||
                        "image/gif".equals(contentType) ||
                        "image/svg".equals(contentType) ||
                        "image/raw".equals(contentType) ||
                        "image/webp".equals(contentType));
    }

    public Result<User> faceLogin(String id, MultipartFile image) {
        if (FaceComparisonUtil.compareFaces(image, getById(id).getImgUrl()) >= 80)
            return Result.success(getUserById(id));
        else
            return Result.error("人脸识别失败！");
    }


}
